package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.AddressBookApi;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.ServeApi;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.orders.dto.request.OrderCancelReqDTO;
import com.jzo2o.api.orders.dto.response.OrderResDTO;
import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.ExecutionResultResDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.constants.UserType;
import com.jzo2o.common.enums.EnableStatusEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderRefundStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.domain.OrdersRefund;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.base.service.IOrdersCommonService;
import com.jzo2o.orders.manager.feignClient.CustomerClient;
import com.jzo2o.orders.manager.model.dto.OrderCancelDTO;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.orders.manager.service.IOrdersRefundService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersManagerServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersManagerService {

    @Override
    public List<Orders> batchQuery(List<Long> ids) {
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public Orders queryById(Long id) {
        return baseMapper.selectById(id);
    }

    /**
     * 滚动分页查询
     *
     * @param currentUserId 当前用户id
     * @param ordersStatus  订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
     * @param sortBy        排序字段
     * @return 订单列表
     */
    @Override
    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
        //1.构件查询条件
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getUserId, currentUserId)
                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus());
        Page<Orders> queryPage = new Page<>();
        queryPage.addOrder(OrderItem.desc(SORT_BY));
        queryPage.setSearchCount(false);

        //2.查询订单列表
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        List<Orders> records = ordersPage.getRecords();
        List<OrderSimpleResDTO> orderSimpleResDTOS = BeanUtil.copyToList(records, OrderSimpleResDTO.class);
        return orderSimpleResDTOS;

    }
    /**
     * 根据订单id查询
     *
     * @param id 订单id
     * @return 订单详情
     */
    @Override
    public OrderResDTO getDetail(Long id) {
        Orders orders = queryById(id);
        OrderResDTO orderResDTO = BeanUtil.toBean(orders, OrderResDTO.class);
        return orderResDTO;
    }

    /**
     * 订单评价
     *
     * @param ordersId 订单id
     */
    @Override
    @Transactional
    public void evaluationOrder(Long ordersId) {
//        //查询订单详情
//        Orders orders = queryById(ordersId);
//
//        //构建订单快照
//        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
//                .evaluationTime(LocalDateTime.now())
//                .build();
//
//        //订单状态变更
//        orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO);
    }

//    @Resource
//    private AddressBookApi addressBookApi;
    @Resource
    private CustomerClient customerClient;
    @Resource
    private ServeApi serveApi;
    @Resource
    private IOrdersManagerService ordersManagerService;

    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO dto) {
        Long addressId = dto.getAddressBookId();
//        根据预约地址id，远程调用查询地址信息
        AddressBookResDTO bookResDTO = customerClient.getAddressBookById(addressId);
        log.info("远程调用查询地址信息，结果为：{}", bookResDTO);
        if (bookResDTO == null){
            log.error("远程调用查询地址信息失败，结束任务");
            throw new CommonException("查询地址信息失败，订单创建失败!");
        }
        Long serveId = dto.getServeId();
//        根据服务id，远程调用查询服务信息
        ServeAggregationResDTO aggregationResDTO = serveApi.findById(serveId);
        log.info("远程调用查询服务信息，结果为：{}", aggregationResDTO);
        if (aggregationResDTO == null){
            log.error("远程调用查询服务信息失败，结束任务");
            throw new CommonException("查询服务信息失败，订单创建失败!");
        }
//        生成订单号
        long orderNo = createOrderNo();
        log.info("生成订单号，结果为：{}", orderNo);
//        获取构造的数量
        Integer purNum = dto.getPurNum();
//        获取优惠券的金额
//        BigDecimal discountAmount = BigDecimal.ZERO;
        BigDecimal discountAmount = new BigDecimal("0");
        if (dto.getCouponId() != null){
//        TODO 修改优惠的金额
        }
//        计算订单金额
        BigDecimal totalAmount = aggregationResDTO.getPrice().multiply(BigDecimal.valueOf(purNum));
//        计算实际支付金额
        BigDecimal realPayAmount = totalAmount.subtract(discountAmount);
//        构造收货详细地址
        StringBuilder sr = new StringBuilder(bookResDTO.getProvince())
                .append(bookResDTO.getCity())
                .append(bookResDTO.getCounty())
                .append(bookResDTO.getAddress());
        String address = sr.toString();
//        构造订单对象
        Orders orders = new Orders();
        orders.setUserId(UserContext.currentUserId());
        orders.setId(orderNo);
        orders.setServeAddress(address);
        orders.setServeStartTime(dto.getServeStartTime());
        orders.setServeTypeName(aggregationResDTO.getServeTypeName());
        orders.setServeTypeId(aggregationResDTO.getServeTypeId());
        orders.setServeItemImg(aggregationResDTO.getServeItemImg());
        orders.setServeItemName(aggregationResDTO.getServeItemName());
        orders.setServeItemId(aggregationResDTO.getServeItemId());
        orders.setServeId(aggregationResDTO.getId());
        orders.setPrice(aggregationResDTO.getPrice());
        orders.setPurNum(purNum);
        orders.setUnit(aggregationResDTO.getUnit());
//        设置区域
        orders.setCityCode(aggregationResDTO.getCityCode());
//        设置收货人
        orders.setContactsName(bookResDTO.getName());
        orders.setContactsPhone(bookResDTO.getPhone());
        orders.setLat(bookResDTO.getLat());
        orders.setLon(bookResDTO.getLon());
//        设置金额
        orders.setTotalAmount(totalAmount);
        orders.setRealPayAmount(realPayAmount);
        orders.setDiscountAmount(discountAmount);
//        设置状态
        orders.setOrdersStatus(OrderStatusEnum.NO_PAY.getStatus());
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
//        设置排序
        long sortBy = DateUtils.toEpochMilli(orders.getServeStartTime()) + orders.getId() % 100000;
        orders.setSortBy(sortBy);
//        使用aspectj，获取代理对象
//        IOrdersManagerService managerService = (IOrdersManagerService)AopContext.currentProxy();
//        managerService.saveOrder(orders);
        ordersManagerService.saveOrder(orders);

//        如果保存订单成功了，发送Rabbitmq的延迟消息，延迟15分钟，消息内容，就是订单的id，如果15分钟未支付，则关闭订单
        PlaceOrderResDTO placeOrderResDTO = new PlaceOrderResDTO();
        placeOrderResDTO.setId(orderNo);
        return placeOrderResDTO;
    }

    @Override
    @Transactional
    public void saveOrder(Orders orders){
//        保存订单表
        save(orders);
//        保存订单详情表
//        保存通知表
    }

    @Resource
    private NativePayApi nativePayApi;
    @Resource
    private TradeProperties tradeProperties;
    @Override
    public OrdersPayResDTO OrderPay(Long orderId, OrdersPayReqDTO dto) {
//        获取订单信息
        Orders orders = getById(orderId);
//        判断订单是否存在
        if (orders == null){
            log.error("订单不存在");
            throw new CommonException("订单不存在");
        }
//        判断订单状态，如果是已支付，直接返回
        if (!OrderStatusEnum.NO_PAY.getStatus().equals(orders.getOrdersStatus()) &&
            orders.getTradingOrderNo() != null){
            log.error("订单状态异常");
            throw new CommonException("订单状态异常");
        }
//        判断支付渠道，获取支付的商户号
        Long enterpriseId = dto.getTradingChannel().equals(PayChannelEnum.ALI_PAY)?tradeProperties.getAliEnterpriseId():tradeProperties.getWechatEnterpriseId();
//        远程调用支付服务，获取支付二维码
        NativePayReqDTO nativePayDto = new NativePayReqDTO();
        nativePayDto.setProductOrderNo(orders.getId());
        nativePayDto.setTradingChannel(dto.getTradingChannel());
        nativePayDto.setMemo("云岚到家，" + orders.getServeItemName());
        nativePayDto.setTradingAmount(orders.getRealPayAmount());
        nativePayDto.setProductAppId("jzo2o.orders");
        nativePayDto.setEnterpriseId(enterpriseId); //商户号
        if (!dto.getTradingChannel().equals(orders.getTradingChannel())) {
            nativePayDto.setChangeChannel(true);
        }
        NativePayResDTO payResDTO = nativePayApi.createDownLineTrading(nativePayDto);
//        把支付服务的交易单号更新到订单表中
        boolean b = lambdaUpdate()
                .eq(Orders::getId, orderId)
                .set(Orders::getTradingOrderNo, payResDTO.getTradingOrderNo())
                .set(Orders::getTradingChannel, payResDTO.getTradingChannel())
                .update();
        if (!b){
            log.error("更新订单表失败");
            throw new CommonException("更新订单表失败");
        }
        OrdersPayResDTO ordersPayResDTO = BeanUtil.toBean(payResDTO, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        return ordersPayResDTO;
    }

    @Resource
    private TradingApi tradingApi;
    @Override
    public OrdersPayResDTO payResult(Long id) {
//        查询订单信息
        Orders orders = getById(id);
        if (orders == null){
            log.error("订单不存在");
            throw new CommonException("订单不存在");
        }
        OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
        ordersPayResDTO.setProductOrderNo(id);
        ordersPayResDTO.setTradingOrderNo(orders.getTradingOrderNo());
        ordersPayResDTO.setTradingChannel(orders.getTradingChannel());
        ordersPayResDTO.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
//        如果订单是已支付，直接返回
        if (OrderPayStatusEnum.PAY_SUCCESS.getStatus() == orders.getPayStatus()){
            ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
            return ordersPayResDTO;
        }
        if (OrderPayStatusEnum.NO_PAY.getStatus() == orders.getPayStatus() &&
             orders.getTradingOrderNo() != null){
//        如果是未支付，并且有交易单号，则远程调用支付服务查询支付结果
            TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
//          第三方支付单号校验
            if (ObjectUtil.isEmpty(tradingResDTO.getTransactionId())) {
                throw new CommonException("支付成功通知缺少第三方支付单号");
            }
            if (tradingResDTO.getTradingState().getCode() == TradingStateEnum.YJS.getCode()){
//           如果查询结果是支付成功，修改订单的状态
                boolean b = lambdaUpdate()
                        .eq(Orders::getId, id)
                        .eq(Orders::getOrdersStatus, OrderStatusEnum.NO_PAY.getStatus())
                        .set(Orders::getOrdersStatus,OrderStatusEnum.DISPATCHING.getStatus())
                        .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                        .set(Orders::getPayTime, LocalDateTime.now())
                        .set(Orders::getTradingOrderNo, tradingResDTO.getTradingOrderNo())
                        .set(Orders::getTradingChannel, tradingResDTO.getTradingChannel())
                        .set(Orders::getTransactionId, tradingResDTO.getTransactionId())
                        .update();
                if (!b){
                    log.error("修改订单状态失败");
                    throw new CommonException("修改订单状态失败");
                }
                ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());
            }
        }
        return ordersPayResDTO;
    }

    @Override
    public void cancel(OrderCancelReqDTO dto) {
//        根据id查询订单
        Orders orders = getById(dto.getId());
        if (orders == null){
            log.error("订单不存在");
            throw new CommonException("订单不存在");
        }
        IOrdersManagerService myServiceProxy = (IOrdersManagerService) AopContext.currentProxy();
//        取消订单有2个分支
        if (OrderStatusEnum.NO_PAY.getStatus() == orders.getOrdersStatus() &&
             OrderPayStatusEnum.NO_PAY.getStatus() == orders.getPayStatus()) {
//        未支付状态，直接修改订单状态为已取消
//            避免事务失效
            OrderCancelDTO orderCancelDTO = new OrderCancelDTO();
            orderCancelDTO.setCurrentUserId(UserContext.currentUserId());
            orderCancelDTO.setCurrentUserName(UserContext.currentUser().getName());
            orderCancelDTO.setCurrentUserType(UserType.C_USER);
            orderCancelDTO.setCancelReason(dto.getCancelReason());
            orderCancelDTO.setId(orders.getId());
            myServiceProxy.cancelOrder(orderCancelDTO,OrderStatusEnum.CANCELED.getStatus());
        } else if (OrderStatusEnum.DISPATCHING.getStatus() == orders.getOrdersStatus() &&
                    OrderPayStatusEnum.PAY_SUCCESS.getStatus() == orders.getPayStatus()){
//        如果已支付，状态为待派单，修改为已关闭，并调用支付服务接口，进行退款
            myServiceProxy.closeOrder(dto.getId(),dto.getCancelReason(),OrderStatusEnum.CLOSED.getStatus(),orders.getTradingOrderNo(),orders.getRealPayAmount());
        } else {
            throw new CommonException("订单状态不允许取消");
        }
    }

    @Resource
    private IOrdersCanceledService canceledService;
    @Override
    @Transactional
    public void cancelOrder(OrderCancelDTO orderCancelDTO, Integer status) {
//        修改订单表
        boolean b = lambdaUpdate()
                .eq(Orders::getId, orderCancelDTO.getId())
                .eq(Orders::getOrdersStatus, OrderStatusEnum.NO_PAY.getStatus())
                .set(Orders::getOrdersStatus, status)
                .update();
        if (!b){
            log.error("修改订单状态为已取消失败");
            throw new CommonException("修改订单状态为已取消失败");
        }
//        新增订单取消记录
        OrdersCanceled orderCancel = new OrdersCanceled();
        orderCancel.setId(orderCancelDTO.getId());
        orderCancel.setCancelReason(orderCancelDTO.getCancelReason());
        orderCancel.setCancellerType(orderCancelDTO.getCurrentUserType());
        orderCancel.setCancelTime(LocalDateTime.now());
        orderCancel.setCancellerId(orderCancelDTO.getCurrentUserId());
        orderCancel.setCancelerName(orderCancelDTO.getCurrentUserName());
        boolean b1 = canceledService.save(orderCancel);
        if (!b1){
            log.error("新增订单取消记录失败");
            throw new CommonException("新增订单取消记录失败");
        }
    }

    @Resource
    private IOrdersRefundService refundService;
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void closeOrder(Long id, String cancelReason, Integer status, Long tradingOrderNo, BigDecimal realPayAmount) {
//        新增canceled数据
        OrdersCanceled orderCancel = new OrdersCanceled();
        orderCancel.setId(id);
        orderCancel.setCancelReason(cancelReason);
        orderCancel.setCancellerType(1);
        orderCancel.setCancelTime(LocalDateTime.now());
        orderCancel.setCancellerId(UserContext.currentUserId());
        orderCancel.setCancelerName(UserContext.currentUser().getName());
        boolean b = canceledService.save(orderCancel);
        if (!b){
            log.error("新增订单关闭记录失败");
            throw new CommonException("新增订单关闭记录失败");
        }
//        修改orders的状态
        boolean b1 = lambdaUpdate()
                .eq(Orders::getId, id)
                .eq(Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus())
                .set(Orders::getOrdersStatus, status)
                .update();
        if (!b1){
            log.error("修改订单状态失败");
            throw new CommonException("修改订单状态失败");
        }
//        新增refund表数据
        OrdersRefund orderRefund = new OrdersRefund();
        orderRefund.setId(id);
        orderRefund.setTradingOrderNo(tradingOrderNo);
        orderRefund.setRealPayAmount(realPayAmount);
        boolean b2 = refundService.save(orderRefund);
        if (!b2){
            log.error("新增退款记录失败");
            throw new CommonException("新增退款记录失败");
        }
//        使用异步解耦的方式，调用接口退款

    }

    @Override
    public List<Orders> queryOverTimePayOrdersListByCount(Integer count) {
//        select * from orders where orders_status = ? and create_time < ? limit 100
//        允许待支付时间是15分钟
        List<Orders> ordersList = lambdaQuery()
                .eq(Orders::getOrdersStatus, OrderStatusEnum.NO_PAY.getStatus())
                .lt(Orders::getCreateTime, LocalDateTime.now().minusMinutes(15))
                .last("limit "+count)
                .list();
        return ordersList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrderOverTimeAuto(List<Orders> ordersList) {
        if (CollectionUtil.isEmpty(ordersList)){
            return;
        }
        ordersList.forEach(orders -> {
            OrderCancelDTO orderCancelDTO = new OrderCancelDTO();
            orderCancelDTO.setCurrentUserId(-1L);
            orderCancelDTO.setCurrentUserName("系统自动");
            orderCancelDTO.setCurrentUserType(UserType.SYSTEM);
            orderCancelDTO.setCancelReason("订单超时未支付");
            orderCancelDTO.setId(orders.getId());
            IOrdersManagerService MyServiceProxy = (IOrdersManagerService)AopContext.currentProxy();
            MyServiceProxy.cancelOrder(orderCancelDTO, OrderStatusEnum.CANCELED.getStatus());
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundOrder(Long tradeOrderNo, ExecutionResultResDTO resultResDTO) {
//        判断退款的状态
        Integer refundStatus = resultResDTO.getRefundStatus();
//        修改订单的状态，退款中,成功,失败
        boolean b = lambdaUpdate()
                .eq(Orders::getTradingOrderNo, tradeOrderNo)
                .eq(Orders::getOrdersStatus, OrderStatusEnum.CLOSED.getStatus())
                .set(Orders::getRefundStatus, refundStatus)
                .update();
        if (!b){
            log.error("修改订单状态失败");
            throw new CommonException("修改订单状态失败");
        }
        if (refundStatus == OrderRefundStatusEnum.REFUND_SUCCESS.getStatus()) {
//        只能状态是退款成功，删除退款的记录信息
            LambdaQueryWrapper<OrdersRefund> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(OrdersRefund::getTradingOrderNo, tradeOrderNo);
            boolean b1 = refundService.remove(queryWrapper);
            if (!b1){
                log.error("删除退款记录失败");
                throw new CommonException("删除退款记录失败");
            }
        }
    }


    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 生成自定义的订单号
     * 规则 yyMMdd * 10000000000000 + Redis的自增id
     * @return
     */
    private long createOrderNo() {
        Long prefix = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd");
        Long increment = redisTemplate.opsForValue().increment(RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR, 1);
        long orderNo = prefix * 10000000000000L + increment;
        return orderNo;
    }

}
